#include "Kernel.h"
#include "Hal/IHardware.h"
#include "Hal/x86/Hardware.h"
#include "Hal/x86/CPU.h"
#include "Hal/x86/GDT.h"

using namespace x86;

enum
{
	GDT_MAX_ENTRIES = 3
};

struct GDT_Descriptor
{
	uint16	LimitLow;
	uint16	BaseLow;
	uint8	BaseMiddle;
	uint8	Access;
	uint8	Granularity;
	uint8	BaseHigh;
} _PACKED;

typedef struct GDT_Descriptor GDT_Descriptor;

extern "C"
{
	void CPU_LoadGDT( void * );
	DescriptorTable_Pointer GDT_Pointer;
	GDT_Descriptor GDT_Entries[GDT_MAX_ENTRIES];
}


Result GDT::Initialize( void )
{
	GDT_TRACE( "Initializing...\n" );

	GDT_Pointer.Size = (uint16)( sizeof(GDT_Descriptor) * GDT_MAX_ENTRIES - 1 );
	GDT_Pointer.Address = (uint32)&GDT_Entries[0];

	// null segment
	InitializeEntry( 0, 0, 0, 0 );

	// code segment
	InitializeEntry( 1, 0, 0xFFFFFFFF, GDT_GRANULARITY_4KB | GDT_32BIT |
			GDT_PRESENT | GDT_DESCRIPTOR | GDT_EXECUTABLE | GDT_WRITABLE );

	// data segment
	InitializeEntry( 2, 0, 0xFFFFFFFF, GDT_GRANULARITY_4KB | GDT_32BIT |
			GDT_PRESENT | GDT_DESCRIPTOR | GDT_WRITABLE );

	asm volatile
	(
		"lgdt (%0);"
		"movw $0x10, %%ax;"
		"movw %%ax, %%ds;"
		"movw %%ax, %%es;"
		"movw %%ax, %%fs;"
		"movw %%ax, %%gs;"
		"movw %%ax, %%ss;"
		: : "r"((uint32)&GDT_Pointer) : "%eax"
	);

	return RESULT_OK;
}


void GDT::InitializeEntry( int index , uint32 base, uint32 limit, uint16 flags )
{
	GDT_TRACE( "Initializing Entry, index=%i, base=%i, limit=%i, flags=%i...\n",
        index, base, limit, flags );

	ASSERT( index >= 0 && index < GDT_MAX_ENTRIES );

	GDT_Descriptor * entry = &GDT_Entries[index];

	entry->BaseLow = (uint16)( base & 0xFFFF );
	entry->BaseMiddle = (uint8)( ( base >> 16 ) & 0xFF );
	entry->BaseHigh = (uint8)( ( base >> 24 ) & 0xFF );
	entry->LimitLow = (uint16)( limit & 0xFFFF );
	entry->Granularity = (uint8)( ( limit >> 16) & 0x0F );
	entry->Granularity |= (uint8)( ( flags >> 4 ) & 0xF0 );
	entry->Access = (uint8)( flags & 0xFF );
}
